home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / Futures / AEThreads / AEThreads.c next >
Encoding:
Text File  |  1995-04-15  |  6.2 KB  |  259 lines  |  [TEXT/MMCC]

  1. //    AEThreads.h
  2. //
  3. //    Copyright © 1993-95 Steve Sisak
  4. //    
  5. //    License is granted to use, modify, make derivative works, and 
  6. //    duplicate this code at will, so long as this notice remains intact.
  7. //
  8.  
  9. #ifndef __AETHREADS__
  10. #include "AEThreads.h"
  11. #endif
  12. #ifndef __ERRORS__
  13. #include <Errors.h>
  14. #endif
  15. #ifndef __APPLEEVENTS__
  16. #include <AppleEvents.h>
  17. #endif
  18. #if __MWERKS__
  19. #if defined(powerc) || defined(__powerc)
  20. #include <CPlusLibPPC.h>
  21. #else
  22. #include <stdlib.h>
  23. #include <CPlusLib68k.h>
  24. #endif
  25. #endif
  26.  
  27. typedef struct AEThreadDesc  AEThreadDesc;
  28. typedef struct AEThreadParam AEThreadParam;
  29. typedef struct AESwapData    AESwapData;
  30.  
  31. // A pointer to this structure is kept in the refcon stored by
  32. // AEInstallEventHandler().  Since they are typically only created at
  33. // application startup time, using a pointer will not fragment the 
  34. // application heap.
  35.  
  36. struct AEThreadDesc                    // Kept in the OS refcon
  37. {
  38.     AEEventHandlerUPP    handler;    // The real handler
  39.     long                refcon;        // The real refcon
  40.     Size                stackSize;    // Stack size for handling event
  41.     ThreadOptions        options;    // Thread options for event
  42.     ThreadID            holder;        // used as a semaphore
  43. };
  44.  
  45. // This structure is used to pass parameters to a new thread during the
  46. // spawning process.  Note that it is passed on the stack and must be 
  47. // kept valid until the thread has run once.
  48.  
  49. struct AEThreadParam                // Used in spawning
  50. {
  51.     const AppleEvent*    event;
  52.     AppleEvent*            reply;
  53.     AEThreadDesc*        desc;
  54.     ThreadID            thread;
  55.     OSErr                result;
  56. };
  57.  
  58. // This structure is used to store any thread context which must be
  59. // saved snd restored as the thread is swapped in and out. You may 
  60. // want to modify this structure and AESwitchInHandler() and
  61. // AESwitchOutHandler() to save more context. In the future, this
  62. // should really be a C++ class
  63.  
  64. struct AESwapData
  65. {
  66.     void*                fTopHandler;    // Top failure handler
  67.  
  68. #ifdef __MWERKS__
  69.     DestructorChain*    fStaticChain;    //
  70. #endif
  71. };
  72.  
  73. pascal void        AESwitchInHandler(ThreadID threadBeingSwitched, void *switchProcParam);
  74. pascal void        AESwitchOutHandler(ThreadID threadBeingSwitched, void *switchProcParam);
  75. pascal OSErr    AESpawnAEThread(const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
  76. pascal long        AEThread(AEThreadParam* parms);
  77.  
  78. AEEventHandlerUPP gSpawnAEThreadUPP = nil;
  79.  
  80. #pragma segment foobar
  81.  
  82. pascal OSErr AEInstallThreadedEventHandler(
  83.     AEEventClass        theAEEventClass,
  84.     AEEventID            theAEEventID,
  85.     AEEventHandlerUPP    proc,
  86.     long                handlerRefcon,
  87.     ThreadOptions        options,
  88.     Size                stacksize)
  89. {
  90.     AEThreadDesc* desc = (AEThreadDesc*) NewPtr(sizeof(AEThreadDesc));
  91.     OSErr          err  = MemError();
  92.     
  93.     if (gSpawnAEThreadUPP == nil)
  94.     {
  95.         gSpawnAEThreadUPP = NewAEEventHandlerProc(AESpawnAEThread);
  96.     }
  97.     
  98.     if (err == noErr)
  99.     {
  100.         desc->handler    = proc;
  101.         desc->refcon    = handlerRefcon;
  102.         desc->stackSize    = stacksize;
  103.         desc->options    = options;
  104.         desc->holder    = kNoThreadID;
  105.  
  106.         err = AEInstallEventHandler(theAEEventClass, theAEEventID, gSpawnAEThreadUPP, (long) desc, false);
  107.     }
  108.     
  109.     return err;
  110. }
  111.  
  112. #pragma segment AEThread
  113.  
  114. pascal void AESwitchInHandler(ThreadID threadBeingSwitched, void *switchProcParam)
  115. {
  116.     AESwapData* swap = (AESwapData*) switchProcParam;
  117.  
  118. #ifdef __MWERKS__
  119.     swap->fStaticChain = __local_destructor_chain;
  120. #endif
  121.  
  122.     // Swap in any additional context here
  123.  
  124. }
  125.  
  126. pascal void AESwitchOutHandler(ThreadID threadBeingSwitched, void *switchProcParam)
  127. {
  128.     AESwapData* swap = (AESwapData*) switchProcParam;
  129.  
  130.     // Swap out any additional context here
  131.  
  132. #ifdef __MWERKS__
  133.     __local_destructor_chain = swap->fStaticChain;
  134. #endif
  135. }
  136.  
  137.  
  138. #define ErrCheck(label, result)  if ((result) != noErr) goto label;
  139.  
  140. pascal long AEThread(AEThreadParam* parms)
  141. {
  142.     AppleEvent        event;            // Original parameters we care about
  143.     AppleEvent        reply;
  144.     AEThreadDesc*    desc;
  145.     OSErr            err;
  146.     OSErr            procErr;
  147.     AESwapData        swap;
  148.  
  149.     event = *parms->event;            // copy these into our own stack frame
  150.     reply = *parms->reply;
  151.     desc  =  parms->desc;
  152.  
  153. #if 0    
  154.     myTopHandler = gTopHandler;        // Save global failure handler
  155.     gTopHandler  = nil;                // don't let failures propagate outside
  156. #endif
  157.  
  158.     ErrCheck(punt, err = SetThreadSwitcher(kCurrentThreadID, AESwitchInHandler,  &swap, true));
  159.     ErrCheck(punt, err = SetThreadSwitcher(kCurrentThreadID, AESwitchOutHandler, &swap, false));
  160.     ErrCheck(punt, err = AESuspendTheCurrentEvent(&event));
  161.  
  162.     parms->result = noErr;        // Let caller know we're ready
  163.  
  164.     // At this point, we need to let our caller return
  165.  
  166.     while (desc->holder != kNoThreadID)
  167.     {
  168.         YieldToThread(desc->holder);
  169.     }
  170.  
  171.     // We are now on our own
  172.     
  173.     procErr = err = CallAEEventHandlerProc(desc->handler, &event, &reply, desc->refcon);
  174.  
  175.         // Since the event was suspended, we need to stuff the error code ourselves    
  176.         // note that there's not much we can do about reporting errors beyond here
  177.         
  178.     err = AEPutAttributePtr(&reply, keyErrorNumber, typeShortInteger, &procErr, sizeof(procErr));
  179.  
  180. #if qDebug
  181.     if (err)
  182.         ProgramBreak("\pAEPutAttributePtr failed installing error code - very bad");
  183. #endif
  184.  
  185.     err = AEResumeTheCurrentEvent(&event, &reply, kAENoDispatch, 0);    // This had better work
  186.  
  187. #if qDebug
  188.     if (err)
  189.         DebugStr("\pAEResumeTheCurrentEvent failed - very bad");
  190. #endif
  191.  
  192. #if 0
  193.     gTopHandler     = myTopHandler;    // Restore global failure handler
  194.     myTopHandler = nil;                // Keep terminator from firing handlers
  195. #endif
  196.  
  197. punt:
  198.     parms->result = err;
  199.     return nil;
  200. }
  201.  
  202. #pragma segment Spawn
  203.  
  204. pascal OSErr AESpawnAEThread(const AppleEvent *event, AppleEvent *reply, long handlerRefcon)
  205. {
  206.     AEThreadParam param;
  207.     
  208.     param.event  = event;
  209.     param.reply  = reply;
  210.     param.desc     = (AEThreadDesc*) handlerRefcon;
  211.     param.thread = kNoThreadID;
  212.  
  213.     if (!param.desc)
  214.     {
  215.         param.result = paramErr;
  216.     }
  217.     else
  218.     {
  219.         // make sure no-one else is trying to start a handler
  220.     
  221.         while (param.desc->holder != kNoThreadID)
  222.         {
  223.             YieldToAnyThread();
  224.         }
  225.         
  226.         // Grab the semaphore
  227.             
  228.         if ((param.result = GetCurrentThread(¶m.desc->holder)) == noErr)
  229.         {
  230.             param.result = NewThread(kCooperativeThread,
  231.                             (ThreadEntryProcPtr) &AEThread,
  232.                             ¶m,
  233.                             param.desc->stackSize,
  234.                             param.desc->options,
  235.                             nil,
  236.                             ¶m.thread);
  237.             
  238.             if (param.result == noErr)
  239.             {
  240.                 param.result = 1;
  241.             
  242.                 do
  243.                 {
  244.                     YieldToThread(param.thread);
  245.                 }
  246.                 while (param.result == 1);    // Wait for thread to pick up parameters
  247.             }
  248.         }
  249.         
  250.         param.desc->holder = kNoThreadID;    // release any claims we have
  251.     }
  252.     
  253.     return param.result;
  254. }
  255.  
  256.  
  257.  
  258.  
  259.